#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <netdb.h>
#define SERVER_PORT 36666
#define SRV_IP 127.0.0.1
#define BUFLEN 512

static int count = 0;   // count for the heartbeat test
static struct itimerval oldtv;

//this program is executed as a parent process for all the other processes
char gates_adr[][28] = {        //real lab nodes addresses
        "tile-0-0.local",
        "tile-0-1.local",
        "tile-0-2.local",
        "tile-0-3.local",
        "tile-1-0.local",
        "tile-1-1.local",
        "tile-1-2.local",
        "tile-1-3.local",
        "tile-2-0.local",
        "tile-2-1.local",
        "tile-2-2.local",
        "tile-2-3.local",
        "tile-3-0.local",
        "tile-3-1.local",
        "tile-3-2.local",
        "tile-3-3.local",
        "tile-4-0.local",
        "tile-4-1.local",
        "tile-4-2.local",
        "tile-4-3.local",
        "tile-5-0.local",
        "tile-5-1.local",
        "tile-5-2.local",
        "tile-5-3.local",
        "tile-6-0.local",
        "tile-6-1.local",
        "tile-6-2.local",
        "tile-6-3.local"        };




struct seg_arguments{
unsigned int minval;	//min number of segments
unsigned int maxval;	//max number of segments
unsigned int thbeat;	//heartbeat time
}arg;





//global variables for all the child processes
pid_t heartbeat_pid = -1 ;
pid_t seglogic_pid = -1 ;
pid_t display_pid = -1 ;
	
char kill_msg;
struct sockaddr_in si_other;
struct in_addr localInterface;
struct sockaddr_in groupSock;
struct sockaddr_in group_sock;
char buf[BUFLEN]="0";
int socket_heartbeat=0;


void diep(char *s)
{
	perror(s);
	exit(1);
}


int kill_me(){
	//killing all own processes and itself on demand

	kill(heartbeat_pid,SIGKILL);
	kill(seglogic_pid,SIGKILL);
	kill(display_pid,SIGKILL);
	exit(0); 		
	return 0;	//i don't see much sense in this line, but it was requested

	//maybe we also should add some cleaning functions before
}


int kill_all(){


	//sending kill_all signal to everyone else by broadcast to port 366666
	//here we have a client
	char buf[3];

  	int  s, i, slen=sizeof(si_other);
  	int ilen = 0;
	int len;

  	if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    	diep("socket");
	
	memset((char *) &groupSock, 0, sizeof(groupSock));
	groupSock.sin_family = AF_INET;
	groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
	groupSock.sin_port = htons(36666);
	
	localInterface.s_addr = inet_addr("127.0.0.1");
	if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
	{
		perror("Setting local interface error");
		exit(1);
	}	
	else
	{
		printf("Setting the local interface...OK\n");
	}

	sprintf(buf, "ALL");
        len = sizeof( struct sockaddr);
        if (sendto(s, buf, 3, 0, (struct sockaddr*)&groupSock, sizeof(groupSock) )==-1)
        diep("sendto()");

	
	kill_me();
	return 0;

}

char kill_receive(){

	//receiving kill signal (A/ALL - all, S/SLF - self)

	struct sockaddr_in si_me, si_other;
	int s, i, slen=sizeof(si_other);
	char kill_tab[3];

	if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)	diep("socket");

	memset((char *) &si_me, 0, sizeof(si_me));
	si_me.sin_family = AF_INET;
	si_me.sin_port = htons(SERVER_PORT);
	si_me.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(s, (struct sockaddr *)&si_me, sizeof(si_me))==-1) diep("bind");
  
	if (recvfrom(s, kill_tab, 3, 0, (struct sockaddr *)&si_other, &slen)==-1) diep("recvfrom()");
	printf("Received packet from %s:%d\nData: %s\n\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), kill_tab);

    	close(s);

	if (kill_tab[0] == 'A' && kill_tab[1] == 'L' && kill_tab[2] == 'L') kill_msg = 'A';
	if (kill_tab[0] == 'S' && kill_tab[1] == 'L' && kill_tab[2] == 'F') kill_msg = 'S';

	return kill_msg;
}




void set_timer()
{	
	struct itimerval itv;
	itv.it_interval.tv_sec = 1;
	itv.it_interval.tv_usec = 0;
	itv.it_value.tv_sec = 1;
	itv.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL, &itv, &oldtv);
}

void signal_handler(int m)
{
	int len;
	count ++;
	printf("%d\n", count);
	printf("Sending packet %d\n", count);
	sprintf(buf, "This is packet %d\n", count);
	len = sizeof( struct sockaddr);
		
	if (sendto(socket_heartbeat, buf, BUFLEN, 0, (struct sockaddr*)&group_sock, sizeof(group_sock) )==-1)
	{
		diep("sendto()");
	}
}

int hostname_to_ip(char * hostname , char* ip)
{
    struct hostent *he;
    struct in_addr **addr_list;
    int i;

    if ( (he = gethostbyname( hostname ) ) == NULL)
    {
        // get the host info
        diep("gethostbyname");
        return -1;
    }
    addr_list = (struct in_addr **) he->h_addr_list;
    for(i = 0; addr_list[i] != NULL; i++)
    {
        //Return the first one;
        strcpy(ip , inet_ntoa(*addr_list[i]) );
        return 0;
    }
    return -1;
}

int send_heartbeat()
{

	struct sockaddr_in si_other;
	struct in_addr localInterface;

	int ret;
	char  ip[BUFLEN], hostname[BUFLEN];
	char loopch = 1;

		
	if ((socket_heartbeat=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    diep("socket");
	
	memset((char *) &group_sock, 0, sizeof(group_sock));
	group_sock.sin_family = AF_INET;
	group_sock.sin_addr.s_addr = inet_addr("227.1.1.1");
	group_sock.sin_port = htons(36667);
	
	// the process itself doesn't need to receive the multicast, so I commnet this piece of code.
	/* 
	if(setsockopt(socket_heartbeat, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)
	{
		perror("Setting IP_MULTICAST_LOOP error");
		close(socket_heartbeat);
		return 1;
	}
	else
	{
		printf("Enabling the loopback...OK.\n");
	}
	*/
	gethostname(hostname, BUFLEN);
	if( -1 == (ret = hostname_to_ip(hostname, ip)) )
	{
		diep("hostname_to_ip");
		return -1;
	}
	
	localInterface.s_addr = inet_addr(ip);
	
	if(setsockopt(socket_heartbeat, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
	{
		perror("Setting local interface error");
		exit(1);
	}	
	else
	{
		printf("Setting the local interface...OK\n");
	}
	
	signal(SIGALRM, signal_handler);
	set_timer();
	while(count < 10000)
	{
	}

	close(socket_heartbeat);
	return 0;
}

int main (int argc, char *argv[]){

//expecting arguments - minval, maxval, theartbeat

if ( argc != 4 ){
	printf("Worm arguments not specified. \nUse: %s minvalue maxvalue theartbeat\n", argv[0]);
	exit(0);
}

arg.minval = atoi(argv[1]);
arg.maxval = atoi(argv[2]);
arg.thbeat = atoi(argv[3]);

if (arg.minval > arg.maxval){
	printf("Specified minvalue bigger than maxvalue.\nUse: %s minvalue maxvalue theartbeat\n", argv[0]);
	exit(0);
}




//all the things this main function should do, making child processes and stuff

	heartbeat_pid = fork();

	if (heartbeat_pid == 0){
	
		
		//executing the heartbeat function
		
		int ret;
		ret = send_heartbeat();

	}

	else if (heartbeat_pid < 0) {

		//we can add this message to log as well
		printf("Failed to create the heartbeat process");
		//maybe we should also invoke the kill_me function when failing to create any process the segment needs?
	}
	
	
	else{
		
		//back in the main process code 
	
	
		seglogic_pid = fork();
	
		if (seglogic_pid == 0){
	
			//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			//executing the seglogic function
			//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	
		}

		else if (seglogic_pid < 0) {

			//we can add this message to log as well
			printf("Failed to create the segment main logic process");
			//maybe we should also invoke the kill_me function when failing to create any process the segment needs?
		}
	
	
		else{
	
			//back in the main process code 
	
			display_pid = fork();
	
			if (display_pid == 0){

				//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
				//executing the display function
				//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			



}

			else if (display_pid < 0) {

				//we can add this message to log as well
				printf("Failed to create the display process");
				//maybe we should also invoke the kill_me function when failing to create any process the segment needs?
			}
	
	
			else{
			
				//back in the main process code 
			
				//main process module - suicide part
			
				while (1){		//this is the killing segments part
					

					kill_msg = kill_receive();


					//in message we should receive: char 'A' if killing all, 'S' if killing self
		
					switch (kill_msg){
						case 'A':		//handling kill_all
							kill_all();
							break;
							
		
						case 'S':    	//handling kill_me
							kill_me();
							break;

						default: break;
					}


				}
			}
		}
	}


}


